package com.supplychain.scm.core.dinger.config;

import com.github.jaemon.dinger.core.DingerConfig;
import com.github.jaemon.dinger.core.DingerHelper;
import com.github.jaemon.dinger.core.entity.*;
import com.github.jaemon.dinger.core.entity.enums.DingerResponseCodeEnum;
import com.github.jaemon.dinger.core.entity.enums.DingerType;
import com.github.jaemon.dinger.core.entity.enums.MessageSubType;
import com.github.jaemon.dinger.exception.AsyncCallException;
import com.github.jaemon.dinger.exception.DingerException;
import com.github.jaemon.dinger.exception.SendMsgException;
import com.github.jaemon.dinger.support.CustomMessage;
import com.github.jaemon.dinger.support.DingerAsyncCallback;
import com.github.jaemon.dinger.support.DingerExceptionCallback;
import com.github.jaemon.dinger.support.DingerIdGenerator;
import com.github.jaemon.dinger.support.client.DingerHttpClient;
import com.github.jaemon.dinger.support.client.MediaTypeEnum;
import com.github.jaemon.dinger.support.sign.DingerSignAlgorithm;
import com.github.jaemon.dinger.support.sign.SignBase;
import com.github.jaemon.dinger.utils.DingerUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;


import java.util.*;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 * @author liyang
 * @version 1.0.0
 * @ClassName DingerRootSend
 * @createTime 09:03:00 2022/11/22
 */
@Slf4j
@Data
public class DingerRootSend extends DingerHelper implements IDingerSender {
    private DingerExceptionCallback dingerExceptionCallback;
    private DingerSignAlgorithm dingerSignAlgorithm;
    private CustomMessage textMessage;
    private CustomMessage markDownMessage;
    private DingerIdGenerator dingerIdGenerator;
    private Executor dingTalkExecutor;
    private DingerAsyncCallback dingerAsyncCallback;
    private DingerHttpClient dingerHttpClient;
    private DingerProperties dingerProperties;
    private ApplicationContext applicationContext;

    protected <T extends MsgType> DingerResponse send(T message, List<IDingerProperties> dingerMessageProperties,boolean defaultRoot) {
        DingerType dingerType = message.getDingerType();
        String dkid = dingerIdGenerator.dingerId();
        Map<DingerType, DingerProperties.Dinger> dingers = dingerProperties.getDingers();
        if (!(dingerProperties.isEnabled() && dingers.containsKey(dingerType))) {
            return DingerResponse.failed(dkid, DingerResponseCodeEnum.DINGER_DISABLED);
        }

        DingerConfig localDinger = getLocalDinger();
        // dinger is null? use global configuration and check whether dinger send
        boolean dingerConfig = localDinger != null;
        try {
            DingerProperties.Dinger dinger;
            if (dingerConfig) {
                dinger = new DingerProperties.Dinger();
                BeanUtils.copyProperties(localDinger, dinger);
                dinger.setAsync(localDinger.getAsyncExecute());
                dinger.setRobotUrl(dingers.get(dingerType).getRobotUrl());
            } else {
                dinger = dingers.get(dingerType);
            }

            List<StringBuilder> webhooks = Lists.newArrayList();
            if(Objects.nonNull(dingerMessageProperties) && dingerMessageProperties.size() > 0) {
                webhooks = dingerMessageProperties.stream().map(properties -> webhookBuild(dkid, StringUtils.hasText(properties.robotUrl()) ? properties.robotUrl() : dingers.get(dingerType).getRobotUrl(), properties.tokenId(), properties.secret(), dingerType, message)).collect(Collectors.toList());
            }
            if(defaultRoot) {
                StringBuilder webhook = webhookBuild(dkid, dinger.getRobotUrl(), dinger.getTokenId(), dinger.getSecret(), dingerType, message);
                webhooks.add(webhook);
            }

            Map<String, String> headers = Maps.newHashMap();
            headers.put("Content-Type", MediaTypeEnum.JSON.type());

            // 异步处理, 直接返回标识id
            if (dinger.isAsync()) {
                List<StringBuilder> finalWebhooks = webhooks;
                dingTalkExecutor.execute(() -> {
                    try {
                        for (StringBuilder webhook : finalWebhooks) {
                            String result = dingerHttpClient.post(
                                    webhook.toString(), headers, message
                            );
                            dingerAsyncCallback.execute(dkid, result);
                        }
                    } catch (Exception e) {
                        this.exceptionCallback(dkid, message, new AsyncCallException(e));
                    }
                });
                return DingerResponse.success(dkid, dkid);
            }
            String response = webhooks.stream().map(webhook -> dingerHttpClient.post(
                    webhook.toString(), headers, message
            )).collect(Collectors.joining(";"));
            return DingerResponse.success(dkid, response);
        } catch (Exception e) {
            this.exceptionCallback(dkid, message, new SendMsgException(e));
            return DingerResponse.failed(dkid, DingerResponseCodeEnum.SEND_MESSAGE_FAILED);
        }
    }

    @SneakyThrows
    private <T extends MsgType> StringBuilder webhookBuild(String dkid, String robotUrl, String tokenId, String secret, DingerType dingerType, T message){
        StringBuilder webhook = new StringBuilder();
        webhook.append(robotUrl).append(tokenId);

        if (log.isDebugEnabled()) {
            log.debug("dingerId={} send message and use dinger={}, tokenId={}.", dkid, dingerType, tokenId);
        }
        // 处理签名问题(只支持DingTalk)
        if (DingerUtils.isNotEmpty((secret))) {
            SignBase sign = dingerSignAlgorithm.sign(secret.trim());

            if (dingerType == DingerType.DINGTALK) {
                webhook.append(sign.transfer());
            } else if (dingerType == DingerType.BYTETALK) {
                message.signAttributes(sign);
            }
        }
        return webhook;
    }

    /**
     * 消息类型校验
     *
     * @param messageSubType
     *              消息类型
     * @return
     *              消息生成器
     */
    private CustomMessage customMessage(MessageSubType messageSubType) {
        return messageSubType == MessageSubType.TEXT ? textMessage : markDownMessage;
    }

    private <T> void exceptionCallback(String dingerId, T message, DingerException ex) {
        DingerCallback dkExCallable = new DingerCallback(dingerId, message, ex);
        dingerExceptionCallback.execute(dkExCallable);
    }

    @Override
    public DingerResponse send(MessageSubType messageSubType, DingerRequest request) {
        return send(dingerProperties.getDefaultDinger(), messageSubType, request);
    }

    @Override
    public DingerResponse send(DingerType dingerType, MessageSubType messageSubType, DingerRequest request) {
        return send(dingerType,messageSubType,request,Lists.newArrayList(),Boolean.TRUE);
    }

    @Override
    public DingerResponse send(MessageSubType messageSubType, DingerRequest request, List<Class<? extends IDingerProperties>> dingerPropertiesClass,boolean defaultRoot) {
        return send(dingerProperties.getDefaultDinger(), messageSubType, request,dingerPropertiesClass,defaultRoot);
    }

    @Override
    public DingerResponse send(DingerType dingerType, MessageSubType messageSubType, DingerRequest request, List<Class<? extends IDingerProperties>> dingerPropertiesClass,boolean defaultRoot) {
        if (!messageSubType.isSupport()) {
            return DingerResponse.failed(DingerResponseCodeEnum.MESSAGE_TYPE_UNSUPPORTED);
        }
        CustomMessage customMessage = customMessage(messageSubType);
        String msgContent = customMessage.message(
                dingerProperties.getProjectId(), request
        );
        request.setContent(msgContent);

        MsgType msgType = messageSubType.msgType(
                dingerType, request
        );
        List<IDingerProperties> dingerPropertiesLists = Lists.newArrayList();
        if(Objects.nonNull(dingerPropertiesClass) && dingerPropertiesClass.size() > 0){
            for (Class<? extends IDingerProperties> propertiesClass : dingerPropertiesClass) {
                IDingerProperties bean = applicationContext.getBean(propertiesClass);
                if(Objects.nonNull(bean)){
                    dingerPropertiesLists.add(bean);
                }
            }
        }
        return send(msgType,dingerPropertiesLists,defaultRoot);
    }

    @Override
    public DingerResponse send(MessageSubType messageSubType, DingerRequest request, Class<? extends IDingerProperties>[] dingerPropertiesClass,boolean defaultRoot) {
        return send(dingerProperties.getDefaultDinger(), messageSubType, request,dingerPropertiesClass,defaultRoot);
    }

    @Override
    public DingerResponse send(DingerType dingerType, MessageSubType messageSubType, DingerRequest request, Class<? extends IDingerProperties>[] dingerPropertiesClass,boolean defaultRoot) {
        if(Objects.isNull(dingerPropertiesClass) || dingerPropertiesClass.length == 0){
            return send(dingerType,messageSubType,request,Lists.newArrayList(),defaultRoot);
        } else {
            return send(dingerType,messageSubType,request,Arrays.stream(dingerPropertiesClass).collect(Collectors.toList()),defaultRoot);
        }
    }
}
